/*
 * Copyright (c) Huawei Technologies Co., Ltd. 2021-2022. All rights reserved.
 * File Name     : roce_bond_register.c
 * Version       : v2.0
 * Created       : 2021/12/7
 * Last Modified : 2022/3/24
 * Description   : The definition of RoCE bond common functions.
 */

#ifdef ROCE_BONDING_EN

#include <rdma/ib_user_verbs.h>
#include <rdma/ib_addr.h>
#include <rdma/ib_cache.h>

#include <net/bonding.h>
#include "bond_common_defs.h"

#include "hinic3_hw.h"
#include "hinic3_srv_nic.h"

#include "roce_bond.h"
#include "roce_cmd.h"
#include "roce_netdev.h"


static struct roce3_hca_info g_roce3_hca_info = { 0 };

static bool g_roce3_bond_ipsurx_en = true;

bool roce3_get_bond_ipsurx_en(void)
{
    return g_roce3_bond_ipsurx_en;
}

void roce3_set_bond_ipsurx_en(bool ipsurx_en)
{
    g_roce3_bond_ipsurx_en = ipsurx_en;
}

void roce3_bond_hca_mutex_lock(void)
{
    mutex_lock(&g_roce3_hca_info.hca_bond_mutex);
}

void roce3_bond_hca_mutex_unlock(void)
{
    mutex_unlock(&g_roce3_hca_info.hca_bond_mutex);
}

void roce3_bond_hca_set_func_info(u8 func_nums, u8 func_type)
{
    g_roce3_hca_info.has_func_per_hca = func_nums;
    g_roce3_hca_info.cur_hca_type = func_type;
}

void roce3_bond_hca_update_info(void)
{
    g_roce3_hca_info.prv_hca_type = g_roce3_hca_info.cur_hca_type;
    g_roce3_hca_info.new_hca = false;

    if (g_roce3_hca_info.cur_dev_idx == g_roce3_hca_info.has_func_per_hca) {
        g_roce3_hca_info.one_hca_info[g_roce3_hca_info.hca_cnt - 1].bond_res_has_alloced = true;
    }
}

bool roce3_bond_res_has_alloced(void)
{
    return g_roce3_hca_info.one_hca_info[g_roce3_hca_info.hca_cnt - 1].bond_res_has_alloced;
}

bool roce3_is_bonded(const struct roce3_bond_device *bond_dev)
{
    return ((bond_dev->flags & ROCE_BOND_FLAG) != 0);
}

static bool roce3_bond_mode_is_supported(const struct roce3_bond_device *bond_dev)
{
    if ((bond_dev->bond_tracker.tx_type != NETDEV_LAG_TX_TYPE_ACTIVEBACKUP) &&
        (bond_dev->bond_tracker.tx_type != NETDEV_LAG_TX_TYPE_HASH)) {
        pr_err("[ROCE, ERR] %s: Failed to support bond mode(%d)\n", __func__, bond_dev->bond_tracker.tx_type);
        return false;
    }

    return true;
}

int roce3_cfg_port_tbl(struct roce3_bond_device *bond_dev, u8 bond_state)
{
    void *hwdev = NULL;
    u16 func_id = 0;
    unsigned int func_idx;
    int ret;

    roce3_bond_for_each_func(func_idx, 0, bond_dev)
    {
        func_id = bond_dev->bond_func[func_idx].func_id;
        hwdev = bond_dev->bond_func[func_idx].hwdev;
        if (hwdev == NULL) {
            pr_err("[ROCE, ERR] %s: Failed to process hwdev[%d] is null\n", __func__, func_idx);
            return -EINVAL;
        }

        ret = roce3_set_port_tbl_bond_state(hwdev, bond_state, func_id);
        if (ret != 0) {
            pr_err("[ROCE, ERR] %s: Failed to set port bond state, ret(%d), bond_state(%d), func_id(%d)\n", __func__,
                ret, bond_state, func_id);
            return -1;
        }
    }

    return 0;
}

static int roce3_destroy_bond(struct roce3_bond_device *bond_dev)
{
    int ret;
    ret = roce3_cfg_port_tbl(bond_dev, ROCE_BOND_PORT_DISABLE);
    if (ret != 0) {
        pr_err("[ROCE, ERR] %s: Failed to config port_tbl, ret(%d)\n", __func__, ret);
        return -1;
    }

    return 0;
}

int roce3_deactivate_bond(struct roce3_bond_device *bond_dev)
{
    int ret;

    ret = roce3_destroy_bond(bond_dev);
    if (ret != 0) {
        pr_err("[ROCE, ERR] %s: Failed to destory bond(%d)\n", __func__, ret);
        return -1;
    }

    bond_dev->flags &= ~ROCE_BOND_FLAG;

    return 0;
}

static void roce3_hca_bond_dev_dealloc(int hca_idx, int free_bond_idx)
{
    struct roce3_bond_device *bond_dev = NULL;

    bond_dev = g_roce3_hca_info.one_hca_info[hca_idx].bond_dev[free_bond_idx];
    if (bond_dev) {
        kfree(bond_dev);
    }
}

void roce3_bond_detach(struct roce3_bond_device *bond_dev)
{
    u16 bond_id = bond_dev->rec_hinic_bond_id;
    int ret;

    if (BOND_ID_IS_INVALID(bond_id)) {
        pr_err("[ROCE, ERR] %s: Record bond id is invalid bond_id:%d\n", __func__, bond_id);
        return;
    }

    bond_dev->bond_attach = 0;
    ret = hinic3_bond_unregister_service_func(HINIC3_BOND_USER_ROCE);
    if (ret != 0) {
        pr_err("[ROCE, ERR] %s: Failed to unregister service func(%d)\n", __func__, ret);
        return;
    }

    ret = hinic3_bond_detach(bond_id, HINIC3_BOND_USER_ROCE);
    if (ret != 0) {
        pr_err("[ROCE, ERR] %s: Failed to detach bond(%d)\n", __func__, ret);
    }
}

void roce3_bond_one_hca_resource_free(int hca_idx, int bond_idx)
{
    struct roce3_bond_device *bond_dev = NULL;

    bond_dev = g_roce3_hca_info.one_hca_info[hca_idx].bond_dev[bond_idx];
    if (bond_dev == NULL) {
        return;
    }

    /* Unregister nb */
    if (bond_dev->nb.notifier_call) {
        unregister_netdevice_notifier(&bond_dev->nb);
        bond_dev->nb.notifier_call = NULL;
    }

    /* Destroy the work queue */
    cancel_delayed_work_sync(&bond_dev->bond_work);

    roce3_bond_detach(bond_dev);

    /* free bond_dev */
    roce3_hca_bond_dev_dealloc(hca_idx, bond_idx);
}

void roce3_bond_remove_all(void)
{
    int max_bond_grps;
    int hca_idx;
    int bond_idx;

    /* traverse hca */
    for (hca_idx = 0; hca_idx < g_roce3_hca_info.hca_cnt; hca_idx++) {
        max_bond_grps = g_roce3_hca_info.one_hca_info[hca_idx].max_bond_grps;

        /* Traverse bond_dev under each hca */
        for (bond_idx = 0; bond_idx < max_bond_grps; bond_idx++) {
            roce3_bond_one_hca_resource_free(hca_idx, bond_idx);
        }
    }
}

static bool roce3_is_owned(const struct roce3_bond_func *bond_func)
{
    return ((bond_func->is_own_flags & ROCE_BOND_FUNC_OWN_FLAG) != 0);
}

static bool roce_bond_check_specify_func(const struct pci_dev *pdev, const struct roce3_bond_device *bond_dev,
    bool get_last_func)
{
    u8 func_idx;

    for (func_idx = 0; func_idx < ROCE_BOND_MAX_FUNCS; func_idx++) {
        if (bond_dev->bond_func[func_idx].pdev != pdev) {
            continue;
        }

        if (get_last_func) {
            if (bond_dev->max_slave_func_id == func_idx) {
                return true;
            }
            return false;
        }

        if (roce3_is_owned(&bond_dev->bond_func[func_idx])) {
            return true;
        }
        return false;
    }

    return false;
}

struct roce3_bond_device *roce3_bond_dev_get(struct pci_dev *pdev, bool get_last_func)
{
    struct roce3_bond_device *bond_dev = NULL;
    int hca_idx;
    int bond_idx;

    for (hca_idx = 0; hca_idx < g_roce3_hca_info.hca_cnt; hca_idx++) {
        for (bond_idx = 0; bond_idx < ROCE_BOND_MAX_GROUPS; bond_idx++) {
            bond_dev = g_roce3_hca_info.one_hca_info[hca_idx].bond_dev[bond_idx];
            if (bond_dev == NULL) {
                continue;
            }

            if (roce_bond_check_specify_func(pdev, bond_dev, get_last_func)) {
                return bond_dev;
            }
        }
    }

    return NULL;
}

struct net_device *roce3_bond_get_netdev(struct roce3_device *rdev)
{
    struct net_device *net_dev = NULL;
    struct roce3_bond_device *bond_dev = NULL;
    unsigned int func_idx;
    int func_use_cnt = 0;

    roce3_bond_hca_mutex_lock();

    bond_dev = roce3_bond_dev_get(rdev->pdev, false);
    if (bond_dev == NULL) {
        goto unlock;
    }

    if (!(bond_dev && roce3_is_bonded(bond_dev))) {
        goto unlock;
    }

    roce3_bond_for_each_func(func_idx, 0, bond_dev)
    {
        func_use_cnt++;
    }

    if (func_use_cnt != bond_dev->slave_cnt) {
        goto unlock;
    }

    if (bond_dev->bond_tracker.tx_type == NETDEV_LAG_TX_TYPE_ACTIVEBACKUP) {
        roce3_bond_for_each_func(func_idx, 0, bond_dev)
        {
            if (bond_dev->bond_tracker.netdev_state[func_idx].tx_enabled == true) {
                net_dev = bond_dev->bond_func[func_idx].netdev;
                break;
            }
        }
    } else {
        roce3_bond_for_each_func(func_idx, 0, bond_dev)
        {
            net_dev = bond_dev->bond_func[func_idx].netdev;
            break;
        }
    }

    if (net_dev) {
        dev_hold(net_dev);
    }

unlock:
    roce3_bond_hca_mutex_unlock();

    return net_dev;
}

void roce3_bond_rr_set_flow(struct roce3_device *rdev, struct roce3_qp *rqp, roce_verbs_qp_attr_s *qp_attr)
{
    struct roce3_bond_device *bond_dev = NULL;
    u32 bond_tx_hash;

    if ((rqp->qp_type == IB_QPT_RC) || (rqp->qp_type == IB_QPT_UC) || (rqp->qp_type == IB_QPT_UD) ||
        (rqp->qp_type == IB_QPT_XRC_INI) || (rqp->qp_type == IB_QPT_XRC_TGT)) {
        if (roce3_bond_is_active(rdev->pdev)) {
            bond_dev = roce3_bond_dev_get(rdev->pdev, false);
            if (bond_dev == NULL) {
                pr_err("[ROCE, ERR] %s: Failed to get bond_dev\n", __func__);
                return;
            }

            if (!roce3_bond_mode_is_supported(bond_dev)) {
                return;
            }

            bond_tx_hash = (u32)atomic_add_return(1, &bond_dev->next_port);
            rqp->tx_hash_value = bond_tx_hash;

            qp_attr->path_info.dw0.bs.bond_tx_hash_value = (u16)bond_tx_hash;
        }
    }
}

static int roce3_bond_modify_mac_tbl_for_sdi(struct roce3_device *rdev, u8 *mac, roce3_modify_mac_tbl modify_mac_tbl)
{
    u16 func_id;
    int ret;

    for (func_id = 0; func_id < SDI_BOND_SLAVES_FUNC_NUM; func_id++) {
        if (func_id == rdev->glb_func_id) {
            continue;
        }

        ret = modify_mac_tbl(rdev->hwdev, mac, ROCE_BOND_RSVD_VLAN_ID, rdev->glb_func_id, func_id);
        if (ret != 0) {
            pr_err("[ROCE, ERR] %s: Failed to modify mac table, ret(%d)\n", __func__, ret);
            return ret;
        }
    }

    return 0;
}

int roce3_add_bond_real_slave_mac(struct roce3_device *rdev, u8 *mac)
{
    struct roce3_bond_device *bond_dev = NULL;
    u16 func_id;
    int ret;

    bond_dev = roce3_bond_dev_get(rdev->pdev, false);
    if (bond_dev == NULL) {
        pr_err("[ROCE, ERR] %s: Failed to find bond_dev\n", __func__);
        return -EINVAL;
    }

    roce3_bond_for_each_func(func_id, 0, bond_dev)
    {
        ret = roce3_add_mac_tbl_mac_entry(rdev->hwdev, mac, ROCE_BOND_RSVD_VLAN_ID, rdev->glb_func_id, func_id);
        if (ret != 0) {
            pr_err("[ROCE, ERR] %s: Failed to add mac_vlan entry, ret(%d)\n", __func__, ret);
            return ret;
        }

        if (func_id != rdev->glb_func_id) {
            /* The IPSU MAC table is used for fast forwarding. Even if the addition fails, the forwarding information
               can still be obtained by checking the MAC table later, without judging the execution result. */
            (void)roce3_add_ipsu_tbl_mac_entry(rdev->hwdev, mac, 0, rdev->glb_func_id,
                bond_dev->bond_func[func_id].er_id);
        }
    }

    if (rdev->sdi_bond_name != NULL) {
        ret = roce3_bond_modify_mac_tbl_for_sdi(rdev, mac, roce3_add_mac_tbl_mac_entry);
        if (ret != 0) {
            pr_err("[ROCE, ERR] %s: Failed to modify mac table of sdi, ret(%d)\n", __func__, ret);
            return ret;
        }
    }

    return 0;
}

int roce3_add_bond_vlan_slave_mac(struct roce3_device *rdev, u8 *mac, u16 vlan_id)
{
    struct roce3_bond_device *bond_dev = NULL;
    u16 func_id;
    int ret;

    bond_dev = roce3_bond_dev_get(rdev->pdev, false);
    if (bond_dev == NULL) {
        pr_err("[ROCE, ERR] %s: No bond_dev found\n", __func__);
        return -EINVAL;
    }

    roce3_bond_for_each_func(func_id, 0, bond_dev)
    {
        if (func_id == rdev->glb_func_id) {
            continue;
        }

        ret = roce3_add_mac_tbl_mac_entry(rdev->hwdev, mac, vlan_id, func_id, func_id);
        if (ret != 0) {
            pr_err("[ROCE, ERR] %s: Failed to add mac_vlan entry, ret(%d)\n", __func__, ret);
            return ret;
        }

        /* The IPSU MAC table is used for fast forwarding. Even if the addition fails, the forwarding information
           can still be obtained by checking the MAC table later, without judging the execution result. */
        (void)roce3_add_ipsu_tbl_mac_entry(rdev->hwdev, mac, vlan_id, rdev->glb_func_id,
            bond_dev->bond_func[func_id].er_id);
    }

    return 0;
}

void roce3_del_bond_real_slave_mac(struct roce3_device *rdev)
{
    struct roce3_bond_device *bond_dev = NULL;
    u16 func_id;

    bond_dev = roce3_bond_dev_get(rdev->pdev, false);
    if (bond_dev == NULL) {
        pr_err("[ROCE, ERR] %s: No bond_dev found\n", __func__);
        return;
    }

    roce3_bond_for_each_func(func_id, 0, bond_dev)
    {
        if (func_id != rdev->glb_func_id) {
            (void)roce3_del_ipsu_tbl_mac_entry(rdev->hwdev, rdev->mac, 0, rdev->glb_func_id,
                bond_dev->bond_func[func_id].er_id);
        }

        (void)roce3_del_mac_tbl_mac_entry(rdev->hwdev, rdev->mac, ROCE_BOND_RSVD_VLAN_ID, rdev->glb_func_id, func_id);
    }

    if (rdev->sdi_bond_name != NULL) {
        (void)roce3_bond_modify_mac_tbl_for_sdi(rdev, rdev->mac, roce3_del_mac_tbl_mac_entry);
    }
}

void roce3_del_bond_vlan_slave_mac(struct roce3_device *rdev, u8 *mac, u16 vlan_id)
{
    struct roce3_bond_device *bond_dev = NULL;
    u16 func_id;

    bond_dev = roce3_bond_dev_get(rdev->pdev, false);
    if (bond_dev == NULL) {
        pr_err("[ROCE, ERR] %s: No bond_dev found\n", __func__);
        return;
    }

    roce3_bond_for_each_func(func_id, 0, bond_dev)
    {
        if (func_id == rdev->glb_func_id) {
            continue;
        }

        (void)roce3_del_ipsu_tbl_mac_entry(rdev->hwdev, mac, vlan_id, rdev->glb_func_id,
            bond_dev->bond_func[func_id].er_id);

        (void)roce3_del_mac_tbl_mac_entry(rdev->hwdev, mac, vlan_id, rdev->glb_func_id, func_id);
    }
}

bool roce3_bond_is_active(struct pci_dev *pdev)
{
    struct roce3_bond_device *bond_dev = NULL;
    bool ret = false;

    if (pdev == NULL) {
        pr_err("[ROCE, ERR] %s: Pdev is NULL\n", __func__);
        return ret;
    }

    bond_dev = roce3_bond_dev_get(pdev, false);
    ret = ((bond_dev != NULL) && roce3_is_bonded(bond_dev));

    return ret;
}

int roce3_bond_is_eth_port_of_netdev(struct roce3_device *rdev, struct net_device *event_ndev)
{
    struct net_device *tmp_ndev = NULL;
    struct roce3_bond_device *bond_dev = NULL;
    unsigned int func_idx;
    int ret;

    /* judge current net device */
    tmp_ndev = rdev->ndev;

    ret = roce3_is_eth_port_of_netdev(tmp_ndev, event_ndev);
    if (ret != 0) {
        return 1;
    }

    /* judge buddy net device */
    roce3_bond_hca_mutex_lock();
    bond_dev = roce3_bond_dev_get(rdev->pdev, false);
    if (bond_dev == NULL) {
        pr_err("[ROCE, ERR] %s: No bond_dev found\n", __func__);
        goto err_unlock;
    }

    roce3_bond_for_each_func(func_idx, 0, bond_dev)
    {
        tmp_ndev = bond_dev->bond_func[func_idx].netdev;
        if (tmp_ndev == NULL) {
            pr_err("[ROCE, ERR] %s: Buddy netdev is null\n", __func__);
            goto err_unlock;
        }

        if (tmp_ndev == rdev->ndev) {
            continue;
        }

        ret = roce3_is_eth_port_of_netdev(tmp_ndev, event_ndev);
        if (ret != 0) {
            roce3_bond_hca_mutex_unlock();
            return 1;
        }
    }

err_unlock:
    roce3_bond_hca_mutex_unlock();
    return 0;
}

void roce3_bond_init_glb_hca_info(void)
{
    if (g_roce3_hca_info.mutex_has_init_flags == false) {
        mutex_init(&g_roce3_hca_info.hca_bond_mutex);
        g_roce3_hca_info.mutex_has_init_flags = true;

        g_roce3_hca_info.prv_hca_type = ROCE_BOND_INVALID_HCA;
        g_roce3_hca_info.cur_hca_type = ROCE_BOND_INVALID_HCA;

        g_roce3_hca_info.hca_finish_scan = false;
    }
}

void roce3_bond_judge_new_hca(void)
{
    if (g_roce3_hca_info.hca_finish_scan) {
        return;
    }

    if ((g_roce3_hca_info.cur_hca_type != g_roce3_hca_info.prv_hca_type) ||
        ((g_roce3_hca_info.cur_hca_type == g_roce3_hca_info.prv_hca_type) &&
        (g_roce3_hca_info.cur_dev_idx == g_roce3_hca_info.has_func_per_hca))) {
        g_roce3_hca_info.new_hca = true;
        g_roce3_hca_info.cur_dev_idx = 0;
        ++g_roce3_hca_info.hca_cnt;
    }
}

static int roce3_bond_has_groups(void)
{
    int bond_grps;

    switch (g_roce3_hca_info.has_func_per_hca) {
        case ROCE_BOND_2_FUNC_NUM:
            bond_grps = ROCE_BOND_2_100G_MAX_GROUPS;
            break;

        case ROCE_BOND_4_FUNC_NUM:
            bond_grps = ROCE_BOND_4_25G_MAX_GROUPS;
            break;

        default:
            pr_err("[ROCE, ERR] %s: Not supported bond groups(%d)\n", __func__, g_roce3_hca_info.has_func_per_hca);
            return -EINVAL;
    }

    return bond_grps;
}

static void roce3_bond_mgnt_add(struct roce3_device *rdev, int *ptr_hca_idx, int *ptr_dev_idx)
{
    if (g_roce3_hca_info.new_hca) {
        ++(*ptr_hca_idx);
        g_roce3_hca_info.hca_cnt = *ptr_hca_idx;
    }

    ++(*ptr_dev_idx);
    g_roce3_hca_info.one_hca_info[*ptr_hca_idx - 1].dev_cnt = *ptr_dev_idx;
}

/* Allocate bond_dev for the specified hca */
static int roce3_hca_bond_dev_alloc(const int *ptr_hca_idx, u8 alloc_bond_grps)
{
    struct roce3_bond_device *bond_dev = NULL;
    struct roce3_bond_device *exist_bond_dev = NULL;
    u8 alloc_bond_idx;
    u8 free_bond_idx;
    int func_idx;

    for (alloc_bond_idx = 0; alloc_bond_idx < alloc_bond_grps; alloc_bond_idx++) {
        /* already alloc */
        if (g_roce3_hca_info.one_hca_info[*ptr_hca_idx - 1].bond_dev[alloc_bond_idx]) {
            pr_info("[ROCE] %s: Bond_dev[%d] of hca[%d] alloced already, go on\n", __func__, alloc_bond_idx,
                *ptr_hca_idx - 1);
            continue;
        }

        /* new alloc */
        bond_dev = roce3_bond_dev_alloc();
        if (bond_dev == NULL) {
            pr_err("[ROCE, ERR] %s: Failed to alloc bond_dev[%d] of hca[%d]\n", __func__, alloc_bond_idx,
                (*ptr_hca_idx - 1));
            goto err_bond_dev_alloc;
        }

        bond_dev->hca_id = (u8)(*ptr_hca_idx - 1);
        bond_dev->bond_id = alloc_bond_idx;
        for (func_idx = 0; func_idx < ROCE_BOND_MAX_FUNCS; func_idx++) {
            bond_dev->port_map[func_idx] = -1;
        }
        g_roce3_hca_info.one_hca_info[*ptr_hca_idx - 1].bond_dev[alloc_bond_idx] = bond_dev;
        g_roce3_hca_info.one_hca_info[*ptr_hca_idx - 1].max_bond_grps = alloc_bond_grps;
    }

    return 0;

err_bond_dev_alloc:
    /* Release the work queue and bond_dev previously applied for under the hca */
    for (free_bond_idx = 0; free_bond_idx < alloc_bond_idx; free_bond_idx++) {
        exist_bond_dev = g_roce3_hca_info.one_hca_info[*ptr_hca_idx - 1].bond_dev[free_bond_idx];
        if (exist_bond_dev != NULL) {
            cancel_delayed_work_sync(&exist_bond_dev->bond_work);

            kfree(exist_bond_dev);
        }
    }

    return -ENOMEM;
}

static void roce3_bond_dev_add_func(struct roce3_bond_device *bond_dev, struct roce3_device *rdev)
{
    u8 bond_idx = bond_dev->bond_id;
    u8 func_idx;
    int func_valid_mask;

    for (func_idx = 0; func_idx < ROCE_BOND_MAX_FUNCS; func_idx++) {
        /* Extract the bond model set by the user and confirm whether the current device matches */
        func_valid_mask = (int)(((u32)rdev->want_bond_slave_bits[bond_idx]) & (1 << (u32)func_idx));
        if (func_valid_mask == 0) {
            continue;
        }
        if ((func_idx == rdev->glb_func_id) && (bond_dev->slave_cnt < rdev->want_bond_slave_cnt)) {
            bond_dev->bond_func[func_idx].pdev = rdev->pdev;
            bond_dev->bond_func[func_idx].netdev = rdev->ndev;
            bond_dev->bond_func[func_idx].func_id = rdev->glb_func_id;
            bond_dev->bond_func[func_idx].hwdev = rdev->hwdev;
            bond_dev->bond_func[func_idx].lld_dev = rdev->lld_dev;
            bond_dev->bond_func[func_idx].bond_id = bond_idx;
            bond_dev->bond_func[func_idx].er_id = hinic3_er_id(rdev->hwdev);
            bond_dev->bond_func[func_idx].is_own_flags |= ROCE_BOND_FUNC_OWN_FLAG;
            bond_dev->bond_func[func_idx].is_ppf = (hinic3_func_type(rdev->hwdev) == TYPE_PPF);
            bond_dev->bond_tracker.netdev_state[func_idx].link_up = 0;
            bond_dev->bond_tracker.netdev_state[func_idx].tx_enabled = 0;
            bond_dev->slave_cnt++;
            bond_dev->func_use_bits |= (1 << (u32)func_idx);
            bond_dev->master_dev_idx = -1;
            bond_dev->max_slave_func_id =
                (bond_dev->max_slave_func_id <= func_idx) ? func_idx : bond_dev->max_slave_func_id;
        }
    }
}

static int roce3_bond_event_cb(struct roce3_bond_device *bond_dev)
{
    if (bond_dev->nb.notifier_call != NULL) {
        return 0;
    }

    bond_dev->nb.notifier_call = roce3_bond_netdev_event;
    if (register_netdevice_notifier(&bond_dev->nb) != 0) {
        pr_err("[ROCE, ERR] %s: Failed to register bond_dev[%d] of hca[%d] nb\n", __func__, bond_dev->bond_id,
            bond_dev->hca_id);
        bond_dev->nb.notifier_call = NULL;
        return -1;
    }

    return 0;
}

static void roce3_bond_free_resource(int bond_idx, const int *ptr_hca_idx)
{
    int free_bond_idx;
    int i;
    struct roce3_bond_device *exist_bond_dev = NULL;

    /* Release the related resources of this hca */
    for (free_bond_idx = 0; free_bond_idx <= bond_idx; free_bond_idx++) {
        /* Release the existing work queue and bond_dev resources */
        if (bond_idx != 0) {
            roce3_bond_one_hca_resource_free(*ptr_hca_idx - 1, free_bond_idx);
            continue;
        }

        for (i = 0; i < g_roce3_hca_info.one_hca_info[*ptr_hca_idx - 1].max_bond_grps; i++) {
            exist_bond_dev = g_roce3_hca_info.one_hca_info[*ptr_hca_idx - 1].bond_dev[i];
            if (exist_bond_dev != NULL) {
                cancel_delayed_work_sync(&exist_bond_dev->bond_work);

                kfree(exist_bond_dev);
            }
        }
    }
}

int roce3_bond_add(struct roce3_device *rdev)
{
    struct roce3_bond_device *bond_dev = NULL;
    int max_bond_grps;
    int bond_idx;
    int ret;
    int *ptr_hca_idx = &g_roce3_hca_info.cur_hca_idx;
    int *ptr_dev_idx = &g_roce3_hca_info.cur_dev_idx;

    if (*ptr_dev_idx >= g_roce3_hca_info.has_func_per_hca) {
        pr_err("[ROCE, ERR] %s: Invalid dev_idx(%d)\n", __func__, *ptr_dev_idx);
        return -EINVAL;
    }

    max_bond_grps = roce3_bond_has_groups();
    if (max_bond_grps < 0) {
        pr_err("[ROCE, ERR] %s: Not supported bond groups\n", __func__);
        return -EINVAL;
    }

    /* Manage hca and bond_dev under it */
    roce3_bond_mgnt_add(rdev, ptr_hca_idx, ptr_dev_idx);

    /* distribute bond_dev */
    ret = roce3_hca_bond_dev_alloc(ptr_hca_idx, (u8)max_bond_grps);
    if (ret != 0) {
        pr_err("[ROCE, ERR] %s: Failed to alloc bond_dev of hca[%d]\n", __func__, *ptr_hca_idx - 1);
        return -ENOMEM;
    }

    for (bond_idx = 0; bond_idx < max_bond_grps; bond_idx++) {
        bond_dev = g_roce3_hca_info.one_hca_info[*ptr_hca_idx - 1].bond_dev[bond_idx];

        /* Associate function with bond_dev */
        roce3_bond_dev_add_func(bond_dev, rdev);

        /* hook event handler */
        ret = roce3_bond_event_cb(bond_dev);
        if (ret != 0) {
            pr_err("[ROCE, ERR] %s: Failed to add func[%d] to bond_dev[%d] of hca[%d]\n", __func__, *ptr_dev_idx - 1,
                bond_idx, *ptr_hca_idx - 1);
            goto err_bond_event_cb;
        }
    }

    return 0;

err_bond_event_cb:
    roce3_bond_free_resource(bond_idx, ptr_hca_idx);

    return ret;
}

int roce3_bond_event_cfg_rdev(struct hinic3_lld_dev *lld_dev, void *uld_dev, struct roce3_device **rdev)
{
    struct roce3_bond_device *bond_dev = NULL;

    if (lld_dev == NULL) {
        pr_err("[ROCE, ERR] %s: Lld_dev is null\n", __func__);
        return -EINVAL;
    }

    if (uld_dev != NULL) {
        *rdev = (struct roce3_device *)uld_dev;
        return 0;
    }

    if (!roce3_bond_is_active(lld_dev->pdev)) {
        pr_err("[ROCE, ERR] %s: Bond is not active.\n", __func__);
        return -EINVAL;
    }

    bond_dev = roce3_bond_dev_get(lld_dev->pdev, false);
    if (bond_dev == NULL) {
        pr_err("[ROCE, ERR] %s: No bond_dev found\n", __func__);
        return -EINVAL;
    }

    if (bond_dev->rdev == NULL) {
        pr_err("[ROCE, ERR] %s: Rdev not found.\n", __func__);
        return -EINVAL;
    }

    *rdev = bond_dev->rdev;
    return 0;
}

int roce3_bonded_port_event_report(struct roce3_device *rdev, const struct hinic3_event_info *event)
{
    u32 type = HINIC3_SRV_EVENT_TYPE(event->service, event->type);
    if ((type != HINIC3_SRV_EVENT_TYPE(EVENT_SRV_NIC, EVENT_NIC_LINK_UP)) &&
        (type != HINIC3_SRV_EVENT_TYPE(EVENT_SRV_NIC, EVENT_NIC_LINK_DOWN)) &&
        (type != HINIC3_SRV_EVENT_TYPE(EVENT_SRV_NIC, EVENT_NIC_BOND_UP)) &&
        (type != HINIC3_SRV_EVENT_TYPE(EVENT_SRV_NIC, EVENT_NIC_BOND_DOWN))) {
        return -ERANGE;
    }

    if (roce3_bond_is_active(rdev->pdev)) {
        roce3_handle_bonded_port_state_event(rdev);
    }
    return 0;
}

static void roce3_bond_dev_remove_func(struct roce3_bond_device *bond_dev, struct roce3_device *rdev)
{
    u8 bond_idx = bond_dev->bond_id;
    u8 func_idx;
    u8 last_func_idx_max = -1;
    int func_valid_mask;

    roce3_bond_hca_mutex_lock();
    for (func_idx = 0; func_idx < ROCE_BOND_MAX_FUNCS; func_idx++) {
        /* Extract the bond model set by the user and confirm whether the current device matches */
        func_valid_mask = (int)(((u32)rdev->want_bond_slave_bits[bond_idx]) & (1 << (u32)func_idx));
        if (func_valid_mask == 0) {
            continue;
        }

        if (func_idx == rdev->glb_func_id) {
            memset_s(&bond_dev->bond_func[func_idx], sizeof(struct roce3_bond_func), 0, sizeof(struct roce3_bond_func));

            bond_dev->slave_cnt--;
            bond_dev->func_use_bits &= ~(1 << (u32)func_idx);
            bond_dev->max_slave_func_id = (bond_dev->max_slave_func_id == func_idx) ? last_func_idx_max :
                bond_dev->max_slave_func_id;
        } else {
            last_func_idx_max = func_idx;
        }
    }
    roce3_bond_hca_mutex_unlock();
}

void roce3_bond_remove(struct roce3_device *rdev)
{
    struct roce3_bond_device *bond_dev = NULL;
    int hca_idx;
    int bond_idx;

    g_roce3_hca_info.cur_dev_idx--;

    for (hca_idx = 0; hca_idx < g_roce3_hca_info.hca_cnt; hca_idx++) {
        for (bond_idx = 0; bond_idx < ROCE_BOND_MAX_GROUPS; bond_idx++) {
            bond_dev = g_roce3_hca_info.one_hca_info[hca_idx].bond_dev[bond_idx];
            if (bond_dev == NULL) {
                continue;
            }
            /* Associate function with bond_dev */
            roce3_bond_dev_remove_func(bond_dev, rdev);
        }
    }

    return;
}

void roce3_bond_set_cfg_rdev(struct roce3_device *rdev, bool clear)
{
    struct roce3_bond_device *bond_dev = NULL;
    if (rdev == NULL) {
        pr_err("[ROCE, ERR] %s: Rdev is NULL\n", __func__);
        return;
    }

    bond_dev = roce3_bond_dev_get(rdev->pdev, false);
    if (bond_dev != NULL) {
        bond_dev->rdev = (clear ? NULL : rdev);
    }
}

struct roce3_bond_device *roce3_find_bond_dev_by_bond_name(const char *bond_name)
{
    struct roce3_bond_device *bond_dev = NULL;
    int hca_idx;
    int bond_idx;

    for (hca_idx = 0; hca_idx < g_roce3_hca_info.hca_cnt; hca_idx++) {
        for (bond_idx = 0; bond_idx < ROCE_BOND_MAX_GROUPS; bond_idx++) {
            bond_dev = g_roce3_hca_info.one_hca_info[hca_idx].bond_dev[bond_idx];
            if (bond_dev == NULL) {
                continue;
            }
            if (strcmp(bond_dev->name, bond_name) == 0) {
                return bond_dev;
            }
        }
    }
    return NULL;
}

static bool roce3_bond_check_func_active(unsigned int active_slave_mask, unsigned int func)
{
    return ((active_slave_mask & (1U << func)) != 0);
}

static enum netdev_lag_tx_type roce3_get_tx_type_by_bond_mode(u16 bond_mode)
{
    switch (bond_mode) {
        case BOND_MODE_8023AD:
        case BOND_MODE_XOR:
            return NETDEV_LAG_TX_TYPE_HASH;
        case BOND_MODE_ACTIVEBACKUP:
            return NETDEV_LAG_TX_TYPE_ACTIVEBACKUP;
        default:
            return NETDEV_LAG_TX_TYPE_UNKNOWN;
    }
}

static void roce3_set_bond_attr(struct roce3_bond_device *bond_dev, struct bond_attr *attr)
{
    int ret;
    unsigned int func_idx;
    u8 active_slave_cnt = 0;

    bond_dev->active_slave_mask = attr->active_slaves;
    roce3_bond_for_each_func(func_idx, 0, bond_dev)
    {
        if (roce3_bond_check_func_active(bond_dev->active_slave_mask, func_idx)) {
            bond_dev->bond_tracker.netdev_state[func_idx].tx_enabled = 1;
            active_slave_cnt++;
        } else {
            bond_dev->bond_tracker.netdev_state[func_idx].tx_enabled = 0;
        }
    }
    ret = memcpy_s(&(bond_dev->bond_attr), sizeof(struct bond_attr), attr, sizeof(struct bond_attr));
    if (ret != 0) {
        pr_err("[ROCE, ERR] %s: Fileld to memcpy bond_attr\n", __func__);
        return;
    }

    bond_dev->active_slave_cnt = active_slave_cnt;
    bond_dev->bond_tracker.tx_type = roce3_get_tx_type_by_bond_mode(attr->bond_mode);
}

static void roce3_set_first_roce_func(struct roce3_bond_device *bond_dev, struct bond_attr *attr)
{
    unsigned int func_idx;

    roce3_bond_for_each_func(func_idx, 0, bond_dev)
    {
        if (bond_dev->bond_func[func_idx].hwdev == NULL) {
            continue;
        }
        if (hinic3_support_roce(bond_dev->bond_func[func_idx].hwdev, NULL)) {
            attr->first_roce_func = func_idx;
            return;
        }
    }
    pr_err("[ROCE, ERR] %s: Fileld to set first_roce_func\n", __func__);
}

void roce3_before_bond_active(const char *bond_name, struct bond_attr *attr)
{
    struct roce3_bond_device *bond_dev = NULL;
    unsigned int func_idx;
    unsigned int ppf_idx;
    bool ppf_support_roce = false;

    bond_dev = roce3_find_bond_dev_by_bond_name(bond_name);
    if ((bond_dev == NULL) || (bond_dev->bond_attach == 0)) {
        return;
    }

    roce3_bond_for_each_func(func_idx, 0, bond_dev)
    {
        if (bond_dev->bond_func[func_idx].hwdev != NULL) {
            if (!bond_dev->bond_func[func_idx].is_ppf) {
                hinic3_detach_service(bond_dev->bond_func[func_idx].lld_dev, SERVICE_T_ROCE);
                continue;
            }
            ppf_support_roce = true;
            ppf_idx = func_idx;
        }
    }
    if (ppf_support_roce) {
        hinic3_detach_service(bond_dev->bond_func[ppf_idx].lld_dev, SERVICE_T_ROCE);
    }

    roce3_set_first_roce_func(bond_dev, attr);
    g_roce3_hca_info.cur_dev_idx -= bond_dev->slave_cnt;
    return;
}

void roce3_after_bond_active(const char *bond_name, struct bond_attr *attr)
{
    int ret;
    struct roce3_bond_device *bond_dev = NULL;
    unsigned int func_idx;

    bond_dev = roce3_find_bond_dev_by_bond_name(bond_name);
    if ((bond_dev == NULL) || (bond_dev->bond_attach == 0)) {
        return;
    }

    roce3_set_bond_attr(bond_dev, attr);
    bond_dev->flags |= ROCE_BOND_FLAG;

    roce3_bond_for_each_func(func_idx, 0, bond_dev)
    {
        if (bond_dev->bond_func[func_idx].hwdev == NULL) {
            continue;
        }
        ret = hinic3_attach_service(bond_dev->bond_func[func_idx].lld_dev, SERVICE_T_ROCE);
        if (ret != 0) {
            pr_err("[ROCE, ERR] %s: Failed to attach roce device, ret(%d), func_id:%u\n", __func__, ret, func_idx);
        }
        break;
    }

    g_roce3_hca_info.cur_dev_idx += bond_dev->slave_cnt;
    return;
}

void roce3_before_bond_modify(const char *bond_name, struct bond_attr *attr)
{
    struct roce3_bond_device *bond_dev = NULL;

    bond_dev = roce3_find_bond_dev_by_bond_name(bond_name);
    if ((bond_dev == NULL) || (bond_dev->bond_attach == 0)) {
        return;
    }

    roce3_set_first_roce_func(bond_dev, attr);
}

void roce3_bond_modify_ipsurx(struct roce3_bond_device *bond_dev, roce3_modify_ipsu_tbl_mac modify_ipsurx)
{
    struct roce3_vlan_dev_list *pos = NULL;
    struct roce3_vlan_dev_list *tmp = NULL;
    struct roce3_device *rdev = bond_dev->rdev;
    unsigned int func_id;

    mutex_lock(&rdev->mac_vlan_mutex);
    list_for_each_entry_safe(pos, tmp, &rdev->mac_vlan_list_head, list)
    {
        roce3_bond_for_each_func(func_id, 0, bond_dev) {
            if (func_id == rdev->glb_func_id) {
                continue;
            }
            modify_ipsurx(rdev->hwdev, pos->mac, pos->vlan_id, rdev->glb_func_id,
                bond_dev->bond_func[func_id].er_id);
        }
    }
    mutex_unlock(&rdev->mac_vlan_mutex);

    roce3_bond_for_each_func(func_id, 0, bond_dev) {
        if (func_id == rdev->glb_func_id) {
            continue;
        }
        modify_ipsurx(rdev->hwdev, rdev->mac, 0, rdev->glb_func_id, bond_dev->bond_func[func_id].er_id);
    }
}

bool roce3_bond_dev_erid_is_active(const struct roce3_device *rdev, const struct bond_attr *attr)
{
    u8 er_id = 0;

    er_id = hinic3_er_id(rdev->hwdev);
    return (((1 << er_id) & attr->active_slaves) == 1);
}

void roce3_after_bond_modify(const char *bond_name, struct bond_attr *attr)
{
    struct roce3_bond_device *bond_dev = NULL;
    struct roce3_device *rdev = NULL;

    bond_dev = roce3_find_bond_dev_by_bond_name(bond_name);
    if ((bond_dev == NULL) || (bond_dev->bond_attach == 0)) {
        return;
    }

    roce3_set_bond_attr(bond_dev, attr);

    rdev = bond_dev->rdev;
    if (rdev == NULL) {
        return;
    }

    if ((attr->active_slaves == attr->slaves) && !roce3_bond_dev_erid_is_active(rdev, attr)) {
        g_roce3_bond_ipsurx_en = false;
    }
}

void roce3_before_bond_deactive(const char *bond_name, struct bond_attr *attr)
{
    struct roce3_bond_device *bond_dev = NULL;
    unsigned int func_idx;

    bond_dev = roce3_find_bond_dev_by_bond_name(bond_name);
    if ((bond_dev == NULL) || (bond_dev->bond_attach == 0)) {
        return;
    }

    roce3_bond_for_each_func(func_idx, 0, bond_dev)
    {
        if (bond_dev->bond_func[func_idx].hwdev != NULL) {
            hinic3_detach_service(bond_dev->bond_func[func_idx].lld_dev, SERVICE_T_ROCE);
            break;
        }
    }

    g_roce3_hca_info.cur_dev_idx -= bond_dev->slave_cnt;
    return;
}

void roce3_after_bond_deactive(const char *bond_name, struct bond_attr *attr)
{
    int ret;
    struct roce3_bond_device *bond_dev = NULL;
    unsigned int func_idx;

    bond_dev = roce3_find_bond_dev_by_bond_name(bond_name);
    if ((bond_dev == NULL) || (bond_dev->bond_attach == 0)) {
        return;
    }

    bond_dev->flags &= ~ROCE_BOND_FLAG;
    bond_dev->bond = NULL;

    roce3_bond_for_each_func(func_idx, 0, bond_dev)
    {
        ret = hinic3_attach_service(bond_dev->bond_func[func_idx].lld_dev, SERVICE_T_ROCE);
        if (ret != 0) {
            pr_err("[ROCE, ERR] %s: Failed to attach roce device, ret(%d), func_id:%u\n", __func__, ret, func_idx);
            continue;
        }
    }

    g_roce3_hca_info.cur_dev_idx += bond_dev->slave_cnt;
}

#endif
